# -*- coding: utf-8 -*-
import types
from collections import Iterable
from functools import wraps
from genericpath import isfile
from os import sep

from tornado.ioloop import IOLoop

from program_top.utilities.csv_and_json_serialisation import pickle_load, pickle_save
from program_top.utilities.environment_and_platform import get_current_environment_pack,\
	class_environment_initialisation
from program_top.utilities.initialisation import load_config
from program_top.utilities.my_datetime import data_buffer_time_format, data_buffer_date_format
from program_top.utilities.my_dir import make_ever_dir

class lazzy_property(property):
	'''
	延迟计算属性的值，装饰器，用例如下
	import math

	class Circle:
		def __init__(self, radius):
			self.radius = radius
	
		@lazzy_property
		def area(self):
			print('Computing area')
			return math.pi * self.radius ** 2
		
		@lazzy_property
		def perimeter(self):
			print('Computing perimeter')
			return 2 * math.pi * self.radius
	'''

	def __init__(self, func):
		self.func=func
	
	def __get__(self, instance, cls):
		if instance is None:
			return self
		else:
			value=self.func(instance)
			setattr(instance, self.func.__name__, value)
			return value
	pass

class mission_list_cache(object):
	'''缓存任务列表类'''
	
	string_serialisation_mapper={'datetime': (lambda x: (x.strftime(data_buffer_time_format))),
								 'date': (lambda x: (x.strftime(data_buffer_date_format))), 'str': (lambda x: x),
								 'int': (lambda x: str(x)), 'float': (lambda x: '%.3f'%x),
								 'timedelta': (lambda x: (str(x.total_seconds())+'sec')),
								 'list': (lambda x: x.__name__)}#参数序列化为字符串的基本映射，要确保能够存成文件
	
	def __init__(self, function_for_each):
		''' 指定函数调用'''
		a=wraps(function_for_each)(self)
		self.function=function_for_each
		self.wrapped_function=a
		pass
	
	def __call__(self, iterable, *args, **kwargs):
		'''调用函数'''
		
		assert isinstance(iterable, Iterable), "请确保首位入参为list或tuple之类可迭代的对象"
		
		iter_type_name=iterable.__class__.__name__
		iter_count=len(iterable)
		
		name_list=[self.function.__name__]
		function_parameter_strings=[self.string_serialisation_mapper[each.__class__.__name__](each) for each in
									args[1:]]#参数做成字符串
		name_list.extend(function_parameter_strings)
		'''
		a=kwargs.keys()
		b=sorted(a)

		if b:
			print(b)
			pass
		'''
		kwargs_parameter_strings=[
			';'.join([each, self.string_serialisation_mapper[kwargs[each].__class__.__name__](kwargs[each])]) for each
			in sorted(kwargs.keys())]
		name_list.extend(kwargs_parameter_strings)
		buffer_relative_name=','.join(name_list)+".pickle3"
		
		try:
			buffer_filename=args[0].environment_pack['class_path']['buffer_dir']+buffer_relative_name
		except KeyError:
			buffer_filename=args[0].environment_pack['instance_path']['buffer_dir']+buffer_relative_name
			pass
		
		if isfile(buffer_filename):#缓存文件如果存在，直接读取缓存文件为结果
			result=pickle_load(buffer_filename)
		else:#缓存文件不存在，调用函数并储存结果为缓存文件
			result=self.__wrapped__(*args, **kwargs)
			
			if result.__len__()<1:#如果没有数据，则不将取出的结果存入缓存文件
				return None
			pickle_save(result, buffer_filename)
		return result
	
	def __get__(self, instance, owner):
		
		if instance is None:
			return self
		else:
			return types.MethodType(self, instance)
	
	pass

def load_class_config(cls):
	'''在类定义时让类自动加载类级别的配置文件'''
	current_env_pack=get_current_environment_pack()
	top_config_path=current_env_pack['runtime_paths']['config_file_dir']
	class_config_path=top_config_path+cls.__name__+sep
	make_ever_dir(class_config_path)
	config_file=class_config_path+"config.json"
	load_config(cls,config_file)
	return cls

class buffered_loading(object):
	'''动态加载缓存修饰类'''
	
	string_serialisation_mapper={'datetime': (lambda x: (x.strftime(data_buffer_time_format))),
		'date': (lambda x: (x.strftime(data_buffer_date_format))), 'str': (lambda x: x), 'int': (lambda x: str(x)),
		'float': (lambda x: '%.3f'%x), 'timedelta': (lambda x: (str(x.total_seconds())+'sec')),
		'list': (lambda x:x.__name__)}#参数序列化为字符串的基本映射，要确保能够存成文件
	
	def __init__(self, func):
		a=wraps(func)(self)
		self.function=func
		self.wrapped_function=a
		#print(id(self))
		pass
	
	def __call__(self, *args, **kwargs):
		'''调用函数'''
		#得出本函数的缓存文件名称
		name_list=[self.function.__name__]
		
		function_parameter_strings=[self.string_serialisation_mapper[each.__class__.__name__](each) for each in args[1:]]#参数做成字符串
		name_list.extend(function_parameter_strings)
		'''
		a=kwargs.keys()
		b=sorted(a)

		if b:
			print(b)
			pass
		'''
		kwargs_parameter_strings=[';'.join([each, self.string_serialisation_mapper[kwargs[each].__class__.__name__](kwargs[each])]) for each in sorted(kwargs.keys())]
		name_list.extend(kwargs_parameter_strings)
		buffer_relative_name=','.join(name_list)+".pickle3"
		
		try:
			buffer_filename=args[0].environment_pack['class_path']['buffer_dir']+buffer_relative_name
		except KeyError:
			buffer_filename=args[0].environment_pack['instance_path']['buffer_dir']+buffer_relative_name
			pass
		
		if isfile(buffer_filename):#缓存文件如果存在，直接读取缓存文件为结果
			result=pickle_load(buffer_filename)
		else:#缓存文件不存在，调用函数并储存结果为缓存文件
			result=self.__wrapped__(*args, **kwargs)
			
			if result.__len__()<1:#如果没有数据，则不将取出的结果存入缓存文件
				return None
			pickle_save(result, buffer_filename)
		return result
	
	def __get__(self, instance, owner):
		k=self.wrapped_function
		
		if instance is None:
			return self
		else:
			return types.MethodType(self, instance)
	
	pass

class as_main(object):
	'''启动一个main函数，在main中将该类创建一个实例，保证此实例不退出'''
	def __init__(self,target_class):
		'''被装饰的类和函数定义时，执行此构造函数'''
		super(as_main, self).__init__()
		self.working_type=target_class
		class_environment_initialisation(self)
		pass
	
	def __call__(self, *args, **kwargs):
		'''被装饰后的函数或类被构造时，执行此call函数'''
		current_instance=self.working_type(*args, **kwargs)
		IOLoop.instance().start()
		pass

